package yartiss.engine.simulation.scheduling.policies.tools;

import java.util.HashSet;
import java.util.Set;

import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.engine.simulation.system.SystemMetrics;

public class FPSlackEnergyManager2 {

	static class Job {

		ITask task;
		long release;
		

		Job(ITask t, long release){
			this.task=t;
			this.release=release;
		}
		
	}

	private Set<Job> getConcernedJob(long date, ITaskSet taskset, ITask tauk) {
		
		Set<Job> jobs = new HashSet<Job>();
		
		long di = tauk.getNextAbsoluteDeadline(date);
		
		di = SystemMetrics.effectiveDeadline(tauk, taskset, di); // ?

		
		for(ITask task : taskset.getHeadSet(tauk, false)){
			
//			if(task.getWcet()!=task.getRemainingCost())
//				System.err.println("QUOI ???");
			
			
			for(long time = date ; time < di ;){
				long x = task.getNextActivation(time);
				if(x<di)
					jobs.add(new Job(task,x));//,i==0?task.getRemainingCost():task.getWcet()));
				time=x;
			}
			
		}
		
		return jobs;
	
		
	}

	
	
	
	public long getSlackEnergy(long date, ITaskSet taskset, IEnergyProfile energyProfile) {

		long slackEnergy = Long.MAX_VALUE;
		//long slackEnergy = energyProfile.getMaxEnergyLevel();
		
		Set<Job> concernedJobs = getConcernedJob(date,taskset,taskset.getActiveTasks(date).first()); 
		
		if (log && date == debugTime)
			System.err.println("Calcul du Se. Nb de jobs : "+concernedJobs.size());
		
		for (Job j : concernedJobs){
			slackEnergy = Math.min(slackEnergy, computeSlackEnergy(date, j, taskset, energyProfile));
		}

		return slackEnergy;
	}

	boolean log = false;
	long debugTime = 4;

	private long computeSlackEnergy(long date, Job job, ITaskSet tasks, IEnergyProfile energyProfile) {

		long slackTime = 0;

		long w1 = date;

		// long di = task.getNextDeadline(date);
		long di = job.release + job.task.getDeadline();

		if (log && date == debugTime)
			System.err.print("di : " + di + " ---- ");

		long slackEnergy = energyProfile.getCurrentEnergyLevel();

		if (log && date == debugTime)
			System.err.print(slackEnergy + " - ");

		long interEnergy=0;
		long fin = w1;
		
		while (w1 <= di) {

			long w2 = w1;
			Pair p = getWorkLoadUntil(date, w2, job, tasks,energyProfile);

			if (log && date == debugTime)
				System.err.println("\nw1 : "+w1+" w2 : "+w2+" inter energie : "+p.energy);
			
			interEnergy = p.energy;

			if (log && date == debugTime)
				System.err.print(p.energy + " - ");

			w1 = date + slackTime + p.time;
			fin=w1;
			if (w1 == w2) {
				long nu = getIdlePeriodLength(date, w2, job, tasks);
				slackTime += nu;
				w1 += nu + 1;
				fin +=nu;
			}

		}

		slackEnergy-=interEnergy;
		
		
		slackEnergy += energyProfile.getPowerRate() * (fin-date) ;
	
		if (log && date == debugTime)
			System.err.println(" = " + slackEnergy);

		return slackEnergy;
	}

	static class Pair {
		long time;
		long energy;
	}

	/**
	 * Compute the total workload between date and w
	 * 
	 * @param w
	 * @param task
	 * @return the total workload until time w
	 */
	private Pair getWorkLoadUntil(long systemDate, long w, Job job, ITaskSet tasks, IEnergyProfile energyProfile) {

		Pair p = new Pair();
		
		p.time += job.task.getWcet();
		p.time += (long) (Math.ceil(Math.max(job.release - job.task.getNextActivation(systemDate), 0) / (double) job.task.getPeriod()) * job.task.getWcet());
		p.energy += p.time * job.task.getEnergyConsumptionRate() ;
		
		for (ITask t : tasks.getHeadSet(job.task, false)) {
			
			//long taskCost = job.task.equals(t)?job.cost:t.getWcet();
			//long taskCost = t.getWcet();
			
			long time = (long) (t.getRemainingCost() + Math.ceil(Math.max(w - t.getNextActivation(systemDate), 0) / (double) t.getPeriod()) * t.getWcet());
			p.time += time;
			// if (task != t)
			//p.energy += time * (t.getEnergyConsumptionRate()-energyProfile.getPowerRate()) ;
			p.energy += time * t.getEnergyConsumptionRate() ;
		}

		// System.err.println("t=" + Simulator.instance().getDate() + " : W"
		// + task.getPriority() + '(' + w + ")=" + inter);
		return p;

	}

	private long getIdlePeriodLength(long currentDate, long t, Job job, ITaskSet tasks) {

		// long idle = Math.min(Math.max(0, task.getNextDeadline(currentDate) -
		// t), getIdleRightPart(currentDate, t, task, tasks));

		long idle = Math.min(Math.max(0, job.release + job.task.getDeadline() - t), getIdleRightPart(currentDate, t, job, tasks));

		return idle;

	}

	private long getIdleRightPart(long currentDate, long w, Job job, ITaskSet tasks) {

		long value = w < job.release ? job.release-w : Long.MAX_VALUE;
		
		
		
		for (ITask t : tasks.getHeadSet(job.task, false)) {
			value = Math.min(
					value,
					(long) Math.max(0, Math.ceil((w - t.getNextActivation(currentDate)) / (double) t.getPeriod())) * t.getPeriod()
							+ t.getNextActivation(currentDate) - w);
		}
		return value;

	}

	//
	// private FPSlackTimeManager timeManager = new FPSlackTimeManager();
	// /**
	// *
	// * @param t
	// * @param taskset
	// * @param energyProfile
	// * @return
	// */
	// boolean log = false;
	// long debug = 21931;
	//
	// public long getSlackEnergy(long t, ITaskSet taskset, IEnergyProfile
	// energyProfile) {
	//
	// if (log)
	// System.out.println("============================================================ slack start");
	//
	// Task higherPriorityTask_t = taskset.getActiveTasks(t).first();
	//
	// long dk = SystemMetrics.effectiveDeadline(higherPriorityTask_t, taskset,
	// higherPriorityTask_t.getNextAbsoluteDeadline(t));
	// long slack = Long.MAX_VALUE;
	//
	// if (log)
	// System.out.println("--------------slack energy for : Pi=" +
	// higherPriorityTask_t.getPriority() + "/Ti=" +
	// higherPriorityTask_t.getPeriod()
	// + " : t=" + t + " dk=" + dk + "   di=" +
	// higherPriorityTask_t.getNextAbsoluteDeadline(t));
	//
	// for (Task task_i : taskset.getHeadSet(higherPriorityTask_t, false)) {
	//
	// long xi = task_i.isActivationTime(t) ? t - 1 : t;
	//
	// do {
	// long di = -1, slack_i = -1;
	//
	// xi = task_i.getNextActivation(xi);
	// if (xi > t && xi < dk) {
	//
	// di = task_i.getNextAbsoluteDeadline(xi);
	// di = SystemMetrics.effectiveDeadline(task_i, taskset, di);
	//
	// slack_i = slack_i(t, xi, dk, di, taskset, task_i, higherPriorityTask_t,
	// energyProfile);
	//
	// if (log)
	// System.out.println(task_i.getPriority() + " xi=" + xi + " di=" + di +
	// " se_i=" + slack_i);
	//
	// slack = Math.min(slack, slack_i);
	// }
	//
	// } while (xi < dk);
	//
	// }
	//
	// if (log)
	// System.out.println("slack=" + slack);
	// return slack;
	// }
	//
	// /**
	// *
	// * @param t
	// * current time
	// * @param xi
	// * activation time of considered job
	// * @param dk
	// * effective deadline of highest priority task at time t
	// * @param di
	// * effective deadline of considered job
	// * @param taskset
	// * the task set
	// * @param task_i
	// * the considered task
	// * @param task_k
	// * highest priority task at time t
	// * @param energyProfile
	// * the energy profile
	// * @return the task i job ?level slack energy at time t
	// */
	// private long slack_i(long t, long xi, long dk, long di, ITaskSet taskset,
	// Task task_i, Task task_k, IEnergyProfile energyProfile) {
	//
	// if (log && t == debug)
	// System.out.println("*** t_" + task_i.getPriority());
	//
	// long slack = energyProfile.getCurrentEnergyLevel() +
	// energyProfile.howMuchCanRecharge(di - t);
	//
	// // if(log)System.out.println(t+" : slack["+t+","+end+"]="+slack);
	// for (Task task_j : taskset.getHeadSet(task_k, false)) {
	//
	// long xj = task_i.isActivationTime(t) ? t - 1 : t;
	// do {
	// long ei = 0;
	// xj = task_j.getNextActivation(xj);
	// if (xj > t && xj < di) {
	// if (task_j.getPriority() <= task_i.getPriority()) {
	// ei = task_j.getWcee();
	// // if(log)
	// if (log && t == debug)
	// System.out.println("yes :__+prio_t" + task_j.getPriority() + "__xj=" + xj
	// + " di=" + di);
	// } else if (xj < xi) {
	//
	// ei = 0;
	// long st = timeManager.getSlackTime(xj, taskset);
	//
	// if (st < xi - xj) {
	// ei = task_j.getWcee();
	// if (log)
	// System.out.print("yes :");
	// } else {
	// ei = 0;
	// if (log)
	// System.out.print("no  :");
	// }
	// if (log)
	// System.out.println("__-prio_t" + task_j.getPriority() + "_xj=" + xj +
	// " st=" + st + "<" + (xi - xj));
	// }
	// slack -= ei;
	// }
	//
	// } while (xj < Math.min(dk, di));
	//
	// if (task_j == task_i)
	// slack += task_i.getWcee();
	// // if(task_j.getPriority() > task_i.getPriority() &&
	// // task_j.isActive()){
	// // long st = timeManager.getSlackTime(t, taskset);
	// // //System.out.println("st2="+st);
	// // if(st < xi - t){
	// // slack -= task_j.getRemainingCost() *
	// // task_j.getEnergyConsumptionRate();
	// // if(log)System.out.print("yes :");
	// // }
	// // else if(log)System.out.print("no  :");
	// //
	// if(log)System.out.println("__-prio_avant_t_t"+task_j.getPriority()+" rc="+task_j.getRemainingCost()+" st="+st+"<"+(xi
	// // - t));
	// // }
	// }
	//
	// slack += task_i.getWcee();
	//
	// if (log && t == debug)
	// System.out.println(xi + " : " + slack);
	//
	// return slack;
	// }
	//
	// // public boolean isSlackEnergyDefined(long t, ITaskSet taskset,
	// // SortedSet<Task> activeTasks) {
	// // if(activeTasks.isEmpty())return false;
	// // Task higherPriorityTask_t = activeTasks.first();
	// // long dk = higherPriorityTask_t.getNextAbsoluteDeadline(t);
	// //
	// if(log)System.out.println("##### "+dk+" t="+t+" tache="+higherPriorityTask_t.getPriority());
	// // boolean is = false;
	// // for (Task task : taskset.getHeadSet(higherPriorityTask_t, false)) {
	// // if(task.isActivationTime(t)){
	// // if(log)System.out.println("##### non c pas lui");
	// // return false;
	// // }
	// // if(!is){
	// // long xi = task.getNextActivation(t);
	// // if(xi > t && xi < dk){
	// // if(log
	// //
	// )System.out.println("##### time="+t+"  tk="+higherPriorityTask_t.getPriority()+" peut etre se � cause de : t"+task.getPriority()+" x="+xi+" dk="+dk);
	// // is = true;
	// // }
	// // }
	// // }
	// // if(is && log)System.out.println("##### oui c lui");
	// // return is;
	// // }

}
